/*****************************************************************************
 * ijksdl_vout_android_nativewindow.c
 *****************************************************************************
 *
 * Copyright (c) 2013 Bilibili
 * copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
 *
 * This file is part of ijkPlayer.
 *
 * ijkPlayer is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * ijkPlayer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with ijkPlayer; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "ijksdl_vout_SDL_window.h"

#include <assert.h>
//#include <android/native_window.h>
#include "ijksdl/ijksdl_vout.h"
#include "ijksdl/ijksdl_vout_internal.h"
#include "ijksdl/ijksdl_container.h"
//#include "ijksdl/ijksdl_egl.h"
#include "ijksdl/ffmpeg/ijksdl_vout_overlay_ffmpeg.h"
//#include "ijksdl_codec_android_mediacodec.h"
//#include "ijksdl_inc_internal_android.h"
//#include "ijksdl_vout_overlay_android_mediacodec.h"
//#include "android_nativewindow.h"


#include <SDL.h>

#ifndef AMCTRACE
#define AMCTRACE(...)
#endif

const char program_name[] = "ffplay";
const int default_width  = 640;
const int default_height = 480;

typedef struct SDL_Vout_Opaque {
//    ANativeWindow   *native_window;
    int              null_SDL_window_warned; // reduce log for null window
//    int              next_buffer_id;

    ISDL_Array       overlay_manager;
    ISDL_Array       overlay_pool;

    int SDL_flags;
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_RendererInfo renderer_info;
    SDL_Texture *texture;
    int screen_width;
    int screen_height;
    int is_full_screen;
} SDL_Vout_Opaque;

static SDL_VoutOverlay *func_create_overlay_l(int width, int height, int frame_format, SDL_Vout *vout)
{
//    switch (frame_format) {
//    case IJK_AV_PIX_FMT__ANDROID_MEDIACODEC:
//        return SDL_VoutAMediaCodec_CreateOverlay(width, height, vout);
//    default:
//        return SDL_VoutFFmpeg_CreateOverlay(width, height, frame_format, vout);
//    }
    SDL_Vout_Opaque *opaque = vout->opaque;
    if (width) {
        opaque->screen_width = width;
        opaque->screen_height = height;
    } else {
        opaque->screen_width = default_width;
        opaque->screen_height = default_height;
    }
    SDL_SetWindowTitle(opaque->window, "ijkplayer SDL");
    SDL_SetWindowSize(opaque->window, opaque->screen_width, opaque->screen_height);
    SDL_SetWindowPosition(opaque->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
    if (opaque->is_full_screen)
        SDL_SetWindowFullscreen(opaque->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
    SDL_ShowWindow(opaque->window);
    return SDL_VoutFFmpeg_CreateOverlay(opaque->screen_width, opaque->screen_height, frame_format, vout);
}

static SDL_VoutOverlay *func_create_overlay(int width, int height, int frame_format, SDL_Vout *vout)
{
    SDL_LockMutex(vout->mutex);
    SDL_VoutOverlay *overlay = func_create_overlay_l(width, height, frame_format, vout);
    SDL_UnlockMutex(vout->mutex);
    return overlay;
}

static void func_free_l(SDL_Vout *vout)
{
    if (!vout)
        return;

    SDL_Vout_Opaque *opaque = vout->opaque;
    if (opaque) {
        if (opaque->renderer)
            SDL_DestroyRenderer(opaque->renderer);
        if (opaque->window)
            SDL_DestroyWindow(opaque->window);
    }

    SDL_Vout_FreeInternal(vout);
}

static void calculate_display_rect(SDL_Rect *rect,
                                   int scr_xleft, int scr_ytop, int scr_width, int scr_height,
                                   int pic_width, int pic_height, AVRational pic_sar)
{
    float aspect_ratio;
    int width, height, x, y;

    if (pic_sar.num == 0)
        aspect_ratio = 0;
    else
        aspect_ratio = av_q2d(pic_sar);

    if (aspect_ratio <= 0.0)
        aspect_ratio = 1.0;
    aspect_ratio *= (float)pic_width / (float)pic_height;

    /* XXX: we suppose the screen has a 1.0 pixel ratio */
    height = scr_height;
    width = lrint(height * aspect_ratio) & ~1;
    if (width > scr_width) {
        width = scr_width;
        height = lrint(width / aspect_ratio) & ~1;
    }
    x = (scr_width - width) / 2;
    y = (scr_height - height) / 2;
    rect->x = scr_xleft + x;
    rect->y = scr_ytop  + y;
    rect->w = FFMAX(width,  1);
    rect->h = FFMAX(height, 1);
}


static int realloc_texture(SDL_Renderer *renderer, SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture)
{
    Uint32 format;
    int access, w, h;
    if (!*texture || SDL_QueryTexture(*texture, &format, &access, &w, &h) < 0 || new_width != w || new_height != h || new_format != format) {
        void *pixels;
        int pitch;
        if (*texture)
            SDL_DestroyTexture(*texture);
        if (!(*texture = SDL_CreateTexture(renderer, new_format, SDL_TEXTUREACCESS_STREAMING, new_width, new_height)))
            return -1;
        if (SDL_SetTextureBlendMode(*texture, blendmode) < 0)
            return -1;
        if (init_texture) {
            if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0)
                return -1;
            memset(pixels, 0, pitch * new_height);
            SDL_UnlockTexture(*texture);
        }
        av_log(NULL, AV_LOG_VERBOSE, "Created %dx%d texture with %s.\n", new_width, new_height, SDL_GetPixelFormatName(new_format));
    }
    return 0;
}

static const struct TextureFormatEntry {
    enum AVPixelFormat format;
    int texture_fmt;
} sdl_texture_format_map[] = {
        { AV_PIX_FMT_RGB8,           SDL_PIXELFORMAT_RGB332 },
        { AV_PIX_FMT_RGB444,         SDL_PIXELFORMAT_RGB444 },
        { AV_PIX_FMT_RGB555,         SDL_PIXELFORMAT_RGB555 },
        { AV_PIX_FMT_BGR555,         SDL_PIXELFORMAT_BGR555 },
        { AV_PIX_FMT_RGB565,         SDL_PIXELFORMAT_RGB565 },
        { AV_PIX_FMT_BGR565,         SDL_PIXELFORMAT_BGR565 },
        { AV_PIX_FMT_RGB24,          SDL_PIXELFORMAT_RGB24 },
        { AV_PIX_FMT_BGR24,          SDL_PIXELFORMAT_BGR24 },
        { AV_PIX_FMT_0RGB32,         SDL_PIXELFORMAT_RGB888 },
        { AV_PIX_FMT_0BGR32,         SDL_PIXELFORMAT_BGR888 },
        { AV_PIX_FMT_NE(RGB0, 0BGR), SDL_PIXELFORMAT_RGBX8888 },
        { AV_PIX_FMT_NE(BGR0, 0RGB), SDL_PIXELFORMAT_BGRX8888 },
        { AV_PIX_FMT_RGB32,          SDL_PIXELFORMAT_ARGB8888 },
        { AV_PIX_FMT_RGB32_1,        SDL_PIXELFORMAT_RGBA8888 },
        { AV_PIX_FMT_BGR32,          SDL_PIXELFORMAT_ABGR8888 },
        { AV_PIX_FMT_BGR32_1,        SDL_PIXELFORMAT_BGRA8888 },
        { AV_PIX_FMT_YUV420P,        SDL_PIXELFORMAT_IYUV },
        { AV_PIX_FMT_YUYV422,        SDL_PIXELFORMAT_YUY2 },
        { AV_PIX_FMT_UYVY422,        SDL_PIXELFORMAT_UYVY },
        { AV_PIX_FMT_NONE,           SDL_PIXELFORMAT_UNKNOWN },
};

void get_sdl_pix_fmt_and_blendmode(int format, Uint32 *sdl_pix_fmt, SDL_BlendMode *sdl_blendmode)
{
    int i;
    *sdl_blendmode = SDL_BLENDMODE_NONE;
    *sdl_pix_fmt = SDL_PIXELFORMAT_UNKNOWN;
    if (format == AV_PIX_FMT_RGB32   ||
        format == AV_PIX_FMT_RGB32_1 ||
        format == AV_PIX_FMT_BGR32   ||
        format == AV_PIX_FMT_BGR32_1)
        *sdl_blendmode = SDL_BLENDMODE_BLEND;
    for (i = 0; i < FF_ARRAY_ELEMS(sdl_texture_format_map) - 1; i++) {
        if (format == sdl_texture_format_map[i].format) {
            *sdl_pix_fmt = sdl_texture_format_map[i].texture_fmt;
            return;
        }
    }
}

static int func_display_overlay_l(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
    SDL_Vout_Opaque *opaque = vout->opaque;
    SDL_Window *SDL_window = opaque->window;

    if (!SDL_window) {
        if (!opaque->null_SDL_window_warned) {
            opaque->null_SDL_window_warned = 1;
            ALOGW("func_display_overlay_l: NULL native_window");
        }
        return -1;
    } else {
        opaque->null_SDL_window_warned = 1;
    }

    if (!overlay) {
        ALOGE("func_display_overlay_l: NULL overlay");
        return -1;
    }

    SDL_SetRenderDrawColor(opaque->renderer, 0, 0, 0, 255);
    SDL_RenderClear(opaque->renderer);

    if (overlay->w <= 0 || overlay->h <= 0) {
        ALOGE("func_display_overlay_l: invalid overlay dimensions(%d, %d)", overlay->w, overlay->h);
        return -1;
    }

    enum AVPixelFormat dst_format = AV_PIX_FMT_NONE;
    switch (overlay->format) {
        case SDL_FCC_YV12:
        case SDL_FCC_I420:
//            if (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUVJ420P) {
//                // ALOGE("direct draw frame");
//                use_linked_frame = 1;
//                dst_format = frame->format;
//            } else
            {
                // ALOGE("copy draw frame");
                dst_format = AV_PIX_FMT_YUV420P;
            }
            break;
        case SDL_FCC_I444P10LE:
//            if (frame->format == AV_PIX_FMT_YUV444P10LE) {
//                // ALOGE("direct draw frame");
//                use_linked_frame = 1;
//                dst_format = frame->format;
//            } else
            {
                // ALOGE("copy draw frame");
                dst_format = AV_PIX_FMT_YUV444P10LE;
            }
            break;
        case SDL_FCC_RV32:
            dst_format = AV_PIX_FMT_0BGR32;
            break;
        case SDL_FCC_RV24:
            dst_format = AV_PIX_FMT_RGB24;
            break;
        case SDL_FCC_RV16:
            dst_format = AV_PIX_FMT_RGB565;
            break;
        default:
            ALOGE("SDL_VoutFFmpeg_ConvertPicture in overlay: unexpected overlay format %s(%d)",
                  (char*)&overlay->format, overlay->format);
            return -1;
    }

    SDL_BlendMode sdl_blendmode = SDL_BLENDMODE_NONE;
    Uint32 sdl_pix_fmt;
    get_sdl_pix_fmt_and_blendmode(dst_format, &sdl_pix_fmt, &sdl_blendmode);
    if (realloc_texture(opaque->renderer, &opaque->texture, sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ? SDL_PIXELFORMAT_ARGB8888 : sdl_pix_fmt,
                        overlay->w, overlay->h, sdl_blendmode, 0) < 0) {
        ALOGE("realloc_texture err!");
        return -1;
    }


    int ret = 0;
    SDL_Rect rect;
    AVRational pic_sar;
    pic_sar.den = overlay->sar_den;
    pic_sar.num = overlay->sar_num;
    int flip_v;
    calculate_display_rect(&rect, 0, 0, opaque->screen_width , opaque->screen_height, overlay->w, overlay->h, pic_sar);

    switch(overlay->format) {
    case SDL_FCC__AMC: {
        // only ANativeWindow support
        //IJK_EGL_terminate(opaque->egl);
        return -1;//SDL_VoutOverlayAMediaCodec_releaseFrame_l(overlay, NULL, true);
    }
    case SDL_FCC_RV24:
    case SDL_FCC_I420:
    case SDL_FCC_I444P10LE: {
        // only GLES support
//        if (opaque->egl)
//            return IJK_EGL_display(opaque->egl, native_window, overlay);
        return -1;
        //break;
    }
    case SDL_FCC_YV12:
    case SDL_FCC_RV16:
    case SDL_FCC_RV32: {
        // both GLES & ANativeWindow support
//        if (vout->overlay_format == SDL_FCC__GLES2 && opaque->egl)
//            return IJK_EGL_display(opaque->egl, native_window, overlay);
        if (overlay->pitches[0] < 0) {
            ret = SDL_UpdateTexture(opaque->texture, NULL, overlay->pixels[0] + overlay->pitches[0] * (overlay->h - 1), - overlay->pitches[0]);
        } else {
            ret = SDL_UpdateTexture(opaque->texture, NULL, overlay->pixels[0], overlay->pitches[0]);
        }
        flip_v =  overlay->pitches[0] < 0;
        SDL_RenderCopyEx(opaque->renderer, opaque->texture, NULL, &rect, 0, NULL, flip_v);
        SDL_RenderPresent(opaque->renderer);
        return ret;
        break;
    }
        default:
            ;
    }


    // fallback to ANativeWindow
//    IJK_EGL_terminate(opaque->egl);
//    return SDL_Android_NativeWindow_display_l(native_window, overlay);
    return -1;
}

static void func_set_ScreenSize(SDL_Vout *vout, int width, int height)
{
    SDL_LockMutex(vout->mutex);
    SDL_Vout_Opaque *opaque = vout->opaque;
    opaque->screen_height = height;
    opaque->screen_width  = width;
    SDL_UnlockMutex(vout->mutex);
}


static int func_display_overlay(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
    SDL_LockMutex(vout->mutex);
    int retval = func_display_overlay_l(vout, overlay);
    SDL_UnlockMutex(vout->mutex);
    return retval;
}

static SDL_Class g_nativewindow_class = {
    .name = "ANativeWindow_Vout",
};

SDL_Vout *SDL_Vout_CreateForSDLWindow()
{
    SDL_Vout *vout = SDL_Vout_CreateInternal(sizeof(SDL_Vout_Opaque));
    if (!vout)
        return NULL;

    SDL_Vout_Opaque *opaque = vout->opaque;
//    if (ISDL_Array__init(&opaque->overlay_manager, 32))
//        goto fail;
//    if (ISDL_Array__init(&opaque->overlay_pool, 32))
//        goto fail;

//    opaque->egl = IJK_EGL_create();
//    if (!opaque->egl)
//        goto fail;

//init sdl
    memset(&opaque->renderer_info, 0, sizeof(opaque->renderer_info));
    opaque->screen_width = default_width;
    opaque->screen_height = default_height;
    opaque->renderer = NULL;
    opaque->window = NULL;
    opaque->texture = NULL;
    opaque->SDL_flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
    opaque->is_full_screen = 0;
    /*
    if (audio_disable)
        flags &= ~SDL_INIT_AUDIO;
    else {
        if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
            SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
    }
    if (display_disable)
        flags &= ~SDL_INIT_VIDEO;
    */
    if (SDL_Init (opaque->SDL_flags)) {
        av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
        av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
        goto fail;
    }

    //if (!display_disable)
    {
        int flags = SDL_WINDOW_HIDDEN;
//        if (borderless)
//            flags |= SDL_WINDOW_BORDERLESS;
//        else
        flags |= SDL_WINDOW_RESIZABLE;
        opaque->window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
        if (opaque->window) {
            opaque->renderer = SDL_CreateRenderer(opaque->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
            if (!opaque->renderer) {
                av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
                opaque->renderer = SDL_CreateRenderer(opaque->window, -1, 0);
            }
            if (opaque->renderer) {
                if (!SDL_GetRendererInfo(opaque->renderer, &opaque->renderer_info))
                    av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", opaque->renderer_info.name);
            }
        }
        if (!opaque->window || !opaque->renderer || !opaque->renderer_info.num_texture_formats) {
            av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());
            goto fail;
        }
    }

    vout->opaque_class    = &g_nativewindow_class;
    vout->create_overlay  = func_create_overlay;
    vout->free_l          = func_free_l;
    vout->display_overlay = func_display_overlay;
    vout->set_screensize  = func_set_ScreenSize;

    return vout;
fail:
    func_free_l(vout);
    return NULL;
}
